package cn.maihe.elg.operation.centers.hymake.handler;

import cn.maihe.elg.operation.centers.hymake.auth.HyMakeCenterAuthConfig;
import cn.maihe.elg.operation.centers.hymake.auth.HyMakeHeaderInfo;
import cn.maihe.elg.operation.centers.hymake.config.HyMakeInfoConfig;
import cn.maihe.elg.operation.centers.hymake.dto.HyMakeBaseReqDTO;
import cn.maihe.elg.operation.centers.hymake.dto.HyMakeBaseRespDTO;
import cn.maihe.elg.operation.centers.hymake.enums.HyMakeRespCodeEnum;
import cn.maihe.elg.operation.centers.hymake.exception.HyMakeException;
import cn.maihe.elg.operation.centers.supports.BaseCenterService;
import cn.maihe.elg.operation.centers.supports.CenterTradeContext;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.utils.ElgLogger;
import cn.maihe.elg.operation.utils.ValidateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;

/**
 * @Description 中心（海迈）入口服务
 * @Author WJH
 * @Date 2021/04/22
 */
@Slf4j
public abstract class HyMakeCenterTradeService<REQ extends HyMakeBaseReqDTO, RESP extends HyMakeBaseRespDTO> extends BaseCenterService<REQ, RESP> {

    private final HyMakeInfoConfig centerInfosConfig;
    protected static final String authConfigKey = "authConfigKey";
    protected static final String headerInfoKey = "headerInfoKey";
    protected static final String reqBodyKey = "reqBodyKey";

    public HyMakeCenterTradeService(HyMakeInfoConfig centerInfosConfig) {
        super();
        this.centerInfosConfig = centerInfosConfig;
    }


    protected HyMakeCenterAuthConfig getCurAuthConfig(CenterTradeContext<REQ, RESP> context) {
        return (HyMakeCenterAuthConfig) context.getAttr(authConfigKey);
    }

    public HyMakeInfoConfig.CenterInfo getCurCenterInfo(CenterTradeContext<REQ, RESP> context) {
        return getCurAuthConfig(context).getCenterInfo();
    }

    /**
     * @param bidBond    保证金单位分
     * @param feeRate    保费费率 默认百分几 ： 三明feeRate默认0.15%，保底300，上不封顶
     * @param minFeeYuan 保底300，上不封顶
     * @param maxFeeYuan 封顶金额 元
     * @return 保费 单位分
     */
    public Long calcGuaranteeFee(Long bidBond, String feeRate, Long minFeeYuan, Long maxFeeYuan) {
        if (bidBond == null || StringUtils.isBlank(feeRate)) {
            throw new RuntimeException("保费计算异常:保证金为空或费率为空");
        }
        //0.01% 转0.0001
        BigDecimal rate = new BigDecimal(feeRate).divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP);

        BigDecimal guaranteeFee = new BigDecimal(bidBond).multiply(rate);
        Long guaranteeFeeFen = guaranteeFee.longValue();
        long minFeeFen = minFeeYuan * 100;
        long maxFeeFen = maxFeeYuan * 100;
        if (guaranteeFeeFen < minFeeFen) {
            guaranteeFeeFen = minFeeFen;
        }else if (guaranteeFeeFen > maxFeeFen) {
            guaranteeFeeFen = maxFeeFen;
        }
        return guaranteeFeeFen;
    }

    public Class<REQ> getReqClass() {
        return reqClass;
    }

    public Class<RESP> getRespClass() {
        return respClass;
    }

    /**
     * 武夷山海迈：
     * 请求报文为业务数据明文的sm2公钥加密后的b64字符串，
     * 请求头中的签名信息：为appid+slat+requestid+timestamp+请求报文解密完明文后的base64编码组合的字符串进行md5值32位的大写字符
     *
     * @param centerNoEnum
     * @param orgCode
     * @param reqParams
     * @param attrMap      附加属性值
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RESP doService(CenterNoEnum centerNoEnum, String orgCode, Map<String, Object> reqParams, Map<String, Object> attrMap) {
        ElgLogger.info(log, log -> log.info("[{}:{}][{}] => 原始请求参数:{} 附属属性:{}", centerNoEnum.getCenterNo(), orgCode, tradeService.tradeType(), reqParams, toJsonString(attrMap)));

        HyMakeCenterAuthConfig authConfig = null;
        try {
            authConfig = this.getAuthConfig(centerNoEnum);
        } catch (Exception e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "参数未配置:" + e.getMessage(), e);
        }
//
//        //头部信息通过附属属性入参
//        HyMakeHeaderInfo headerInfo;
//        try {
//            headerInfo = (HyMakeHeaderInfo) reqParams.get(headerInfoKey);
//            ValidateUtil.validate(headerInfo);
//        } catch (Exception e) {
//            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "请求头参数错误:" + e.getMessage(), e);
//        }


        //反序列化操作
        REQ req = this.buildReq(reqParams);

        //构建上下文
        CenterTradeContext context = buildCenterTradeContext(req, centerNoEnum, orgCode, attrMap);
        context.setAttr(authConfigKey, authConfig);

        //业务参数格式规范校验
        verifyParam(context);

        RESP resp = null;
        try {
            resp = (RESP) processService(context);
            context.setResp(resp);
        } catch (Exception e) {
            if (e instanceof HyMakeException) {
                throw e;
            }
            throw HyMakeException.error(e.getMessage(), e);
        }

        // 响应报文加密加签：：data需要加密处理，加签针对整体报文进行加签,统一入口处处理
        return resp;
    }

    private CenterTradeContext buildCenterTradeContext(REQ req, CenterNoEnum centerNoEnum, String orgCode, Map<String, Object> attrMap) {
        CenterTradeContext context = new CenterTradeContext(req, centerNoEnum.getCenterNo(), orgCode, getCurCenterId(centerNoEnum), getOrgId(orgCode));
        context.putAll(attrMap);
        return context;
    }

    protected HyMakeCenterAuthConfig getAuthConfig(CenterNoEnum centerNoEnum) {
        HyMakeCenterAuthConfig authConfig = centerInfosConfig.getAuthConfig(centerNoEnum);
        Assert.notNull(authConfig, () -> centerNoEnum + " 认证相关参数未配置");
        Assert.notNull(authConfig.getCenterInfo(), () -> centerNoEnum + " 认证相关参数未配置");
        Assert.notNull(authConfig.getHyMakeAuthorization(), () -> centerNoEnum + " 签名验签相关参数未配置");
        Assert.notNull(authConfig.getHyMakeEncryption(), () -> centerNoEnum + " 加解密相关参数未配置");
        return authConfig;
    }


    protected boolean verifySignature(HyMakeCenterAuthConfig authConfig, String clearBizReqJsonBody, HyMakeHeaderInfo headerInfo) {
        try {
            return authConfig.getHyMakeAuthorization().verifySignature(headerInfo.getRequestid(), clearBizReqJsonBody, headerInfo.getSign(), headerInfo.getTimestamp());
        } catch (Exception e) {
            throw HyMakeException.error("验签失败:" + e.getMessage(), e);
        }
    }

    /**
     * 具体业务处理
     *
     * @param context
     */
    public abstract RESP processService(CenterTradeContext<REQ, RESP> context);

    /**
     * 参数校验：参数规范校验，涉及业务需落库的后置processService处理
     *
     * @param context
     */
    protected void verifyParam(CenterTradeContext<REQ, RESP> context) {
        try {
            ValidateUtil.validate(context.getReq());
        } catch (Exception e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "参数校验失败:" + e.getMessage(), e);
        }
    }

    protected REQ buildReq(String decryptJsonBody) {
        try {
            return objectMapper.readValue(decryptJsonBody, this.reqClass);
        } catch (IOException e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "反序列化异常", e);
        }
    }


    protected REQ buildReq(Map<String, Object> clearParams) {
        try {
            REQ req = objectMapper.readValue(objectMapper.writeValueAsBytes(clearParams), this.reqClass);
            return req;
        } catch (IOException e) {
            throw HyMakeException.of(HyMakeRespCodeEnum.FAILURE, "反序列化异常", e);
        }
    }

    /**
     * 重新构建请求参数
     *
     * @param context
     * @return
     */
    protected Map<String, Object> reorganizeParams(Map<String, Object> context) {
        return context;
    }
}
